Tutorial

Web Application Development with Flask

6 steps

Previously, you learnt about Flask and its capabilities, created your development environment, and deployed a basic Flask web application. However, the website currently only displays text on a single page. In this quest, you will be adding more pages, creating dynamic pages, and integrating everything into one.

You will also be introduced to the Jinja templating engine. Jinja provides a powerful and flexible way to generate dynamic web pages in Flask, making it easier to build complex web applications while maintaining a clear separation between the presentation layer and the application logic.

Here, you will be asked several times to run your file to see how your webpage looks like on localhost:5000. What you will need to do is to run the command python app.py in your terminal and refresh your browser window which is on localhost:5000. Each time you are done checking your webpage, be sure to hit Ctrl+C in your terminal to quit the viewing. You will need to do this each time you want to see the changes in your code affect your site.

Helpful prior knowledge

Installing Flask
A Vercel account for deploying your site.


Learning Outcomes

By the end of your learning, you will be able to:

  • Create more routes and pages
  • Use Jinja templating to create dynamic pages
  • Create a templated web app

Tutorial Steps

Total steps: 6

  • Step 1: Routing with Flask

    Previously you created a single route at “/” for a page that displays the text “Hello World!”. Now, we’ll create more routes for more pages, and point them to actual HTML pages.

    For simplicity, let’s start with a new folder to prevent issues of conflict with your previously written code.

    1. Create a folder called Quest2
    2. In Quest2, create a folder named app and a file named requirements.txt
    3. Within the app folder, create 1 file named app.py

    Your folder structure should look like this:

    > Quest2
          > app
             > app.py
          > requirements.txt
    Plain text

    We will need to create a virtual environment. Open a terminal in the Quest2 directory and run the following command to create a virtual environment for our quest. The venv module is a built-in module in Python used for creating virtual environments. Note: If the python3 command indicates that Python is not installed, you can replace python3 with python

    python3 -m venv ./venv/

    To initialise your virtual environment, you will need to enter the following command in your terminal.

    Windows users: .\venv\Scripts\activate.bat

    MacOS and Linux Users: source ./venv/bin/activate

    Then, in requirements.txt, declare the dependencies to install.

    Flask
    Plain text

    Run the following command to install those dependencies.

    pip3 install -r requirements.txt

    In app.py, paste the following code. You will notice this is the same code we used previously.

    from flask import Flask, request
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        return 'Hello World!'
    
    if __name__ == '__main__':
        app.run()
    Python

    Let’s create two more routes (about and contact) in the application. Below line 6, add these routes and their corresponding functions.

    @app.route('/about')
    def about():
        return 'About Page'
    
    @app.route('/contact', methods = ['POST', 'GET'])
    def contact():
        if request.method == 'POST':
            return 'You are using POST'
        elif request.method == 'GET':
            return 'You are using GET'
    Python

    Notice that we define that /contact accepts both GET and POST requests. We can use this later to segregate different Create, Read, Update, Delete (CRUD) features.

    You can refer to the image below to check if you have pasted the code correctly.

    If you run the application now, you may view the pages and their corresponding text. In the terminal window, ensure that you are in the app directory. Then enter the command python3 app.py or python app.py.

    Then, in your browser, go to localhost:5000/contact. You should see the message 'You are using GET'. Once done, press Ctrl+C in your terminal to quit.

  • Step 2: Templating with Jinja

    Currently, all of our pages only return text, no fancy HTML, CSS and JS pages. We want to change that by using Jinja. Jinja is a templating engine for Python web development. It provides a way to generate dynamic HTML pages by combining static content with dynamic data.

    Jinja templates are regular HTML files with additional syntax elements provided by Jinja. They can contain variables, control structures (such as loops and conditionals), filters for modifying data, and template inheritance for reusing common layouts.

    To integrate Jinja with Flask, you typically create a templates directory in your Flask project and place your Jinja templates there (we will do this in the next paragraph). Flask automatically configures Jinja to look for templates in this directory. In your Flask routes or views, you can pass data to the templates and render them using the render_template function provided by Flask.

    In the app folder, create two folders – templates and static. In the templates folder, create the file index.html. In the static folder, create a file named style.css. By now, your folder structure should look like this:

    In app.py, we will edit lines 1 and 2 to import more objects and functions and specify the templates and static file directory. This tells Flask where to find HTML template files and static files like images. Update your file with the code below.

    from flask import Flask, request, render_template
    app = Flask(__name__, template_folder='templates', static_folder='static')
    Python

    Next, let’s create a nice landing page for the website. In index.html, copy and paste the code segment found below. After pasting, be sure to replace all instances of “Stackie1234” with your own StackUp username

    <!DOCTYPE html>
    <html>
    <head>
        <title>Stackie1234</title>
        <link rel="stylesheet" type="text/css" href="/static/style.css">
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
    </head>
    <body>
        <div class="navbar">
            <div class="container">
                <div class="flex row jsplit">
                    <div class="logo">
                        <a href="/">Stackie1234</a>
                    </div>
                    <div class="flex row gap">
                        <a href="/about">About</a>
                        <a href="/contact">Contact</a>
                    </div>
                </div>
            </div>
        </div>
        <div class="container">
            <div class="content">
                <h1 class="purple">Stackie1234</h1>
                <p>I love developing web applications.</p>
            </div>
        </div>
    </body>
    </html>
    
    HTML

    Then, in style.css, copy and paste the style.css code segment found below.

    * {
        font-family: 'Poppins', sans-serif;
        color: white;
    }
    
    body {
        background-color: #121212;
        padding: 0px;
        margin: 0px;
    }
    
    .purple {
        color: #4361ee;
    }
    
    .navbar {
        background-color: #121212;
        padding: 0px;
        margin: 0px;
        position: sticky;
        top: 0px;
        z-index: 1;
        padding: 0px 0px;
    }
    
    .flex {
        display: flex;
    }
    
    .row {
        flex-direction: row;
    }
    
    .jsplit {
        justify-content: space-between;
    }
    
    .gap {
        gap: 10px;
    }
    
    .container {
        width: calc(100% - 40px);
        max-width: 1200px;
        margin: auto;
        padding: 20px 20px;
    }
    CSS

    Next, in app.py edit the “/” route with the following code to render the static index.html.

    @app.route('/')
    def hello_world():
        return render_template('index.html')
    Python

    In your terminal, run app.py (using python app.py) and open your website. It should look similar to the image below.

    However, this is all static, let’s try using Jinja to make our page more dynamic.

    In line 3 of app.py, declare some variables as follows.

    username = "Stackie1234"
    description = "I love developing web applications."
    description += "And this text was created with Jinja"
    Python

    For the username variable, replace it with your own StackUp username.

    Next, edit the render_template function call in the hello_world() function to pass in those variables.

    @app.route('/')
    def hello_world():
        return render_template('index.html', username = username, description = description)
    Python

    The render_template() function works by rendering the HTML file specified while parsing the Jinja syntax. In this case, you are passing the username and description from app.py into the variables “username” and “description” in the template.

    Then in index.html, we use double curly braces and variable names to place the contents of the variables into the page. In index.html, replace all the code with the code snippet below.

    <!DOCTYPE html>
    <html>
    <head>
        <title>{{username}}</title>
        <link rel="stylesheet" type="text/css" href="/static/style.css">
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet">
    </head>
    <body>
        <div class="navbar">
            <div class="container">
                <div class="flex row jsplit">
                    <div class="logo">
                        <a href="/">{{username}}</a>
                    </div>
                    <div class="flex row gap">
                        <a href="/about">About</a>
                        <a href="/contact">Contact</a>
                    </div>
                </div>
            </div>
        </div>
        <div class="container">
            <div class="content">
                <h1 class="purple">{{username}}</h1>
                <p>{{description}}</p>
            </div>
        </div>
    </body>
    </html>
    HTML

    While the number of lines for index.html remains the same, this method of creating dynamic pages with variables saves you the effort, especially when you have to change the username three times.

    Run your app.py file and open your localhost:5000. You should see the values of your variables reflected on the webpage, similar to what you see below. If you do not see this page, do check that you have read the instructions in the Description at the start on running the app.py file. 

    There are many other things we can do with Jinja such as looping through lists and displaying content conditionally that we will cover later on.

  • Step 3: Creating a Templated Web Application

    In the previous step, you used simple templating features with Jinja to present the data from variables on the webpage. Let’s take it one step further.

    In line 7 of app.py, before @app.route('/'), we will be defining a variable projects which is a list variable. Do remember to replace all instances of “<StackUp Username>” with your own StackUp username.

    projects = [
        {
            'id': 1,
            'title': 'E-commerce Website',
            'description': 'A website where you can buy stuff',
            'author': '<StackUp Username>',
            'category': 'Web Development'
        },
        {
            'id': 2,
            'title': 'Flask Website',
            'description': 'A website made with Flask',
            'author': '<StackUp Username>',
            'category': 'Web Development'
        },
        {
            'id': 3,
            'title': 'Cool Mobile App',
            'description': 'An app that does cool stuff',
            'author': '<StackUp Username>',
            'category': 'Mobile Development'
        }
    ]
    Python

    With the code above, we will display this list of projects on our website. First, pass the projects variable into the render_template function. Your hello_world function should look like this.

    @app.route('/')
    def hello_world():
        return render_template('index.html', username = username, description = description, projects = projects)
    Python

    We will only display projects categorised as “Web Development”. Let’s use Jinja to filter those projects.

    First, let’s display all projects. In line 28 of index.html, after </p>, add the following lines to loop through all projects and create HTML elements for them. In other words, the code will be added within the div class content.

    <div class="projects">
        {% for project in projects %}
            <div class="project">
                <h2>{{project.title}}</h2>
                <p>{{project.description}}</p>
                <p class="light">{{project.author}}</p>
            </div>
        {% endfor %}
    </div>
    HTML

    For loops in Jinja are defined by {% <loop> %} and end with {% endfor %}.

    If you run the code and view the website, you will see this.

    Let’s first add some new styles in style.css. These styles will display the projects in a row, and wrap them to the next line when the window size is minimised. It also creates cards for each individual project with some simple colour changes.

    .projects {
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: flex-start;
        gap: 10px;
    }
    
    .project {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        background-color: #222222;
        padding: 20px;
        border-radius: 5px;
        flex-grow: 1;
    }
    
    .light {
        opacity: 0.5;
    }
    CSS

    With the addition of these new rulesets, our webpage has changed. You can refer to the image below to see the differences in website before and after the new style.

    You can also refer to this GitHub gist to check if you are on the right track for app.py

  • Step 4: Filtering the Data

    Next, let’s filter the projects that have the category “Web Development”. To do that, we will need to use conditional. 'If' conditions are defined with {% if <condition> %} and end with {% endif %}. In index.html, edit the Jinja loop to add an if condition in lines 30 and 36, such that the div class projects looks like this:

    {% for project in projects %}
        {% if project.category == "Web Development" %}
            <div class="project">
                <h2>{{project.title}}</h2>
                <p>{{project.description}}</p>
                <p class="light">{{project.author}}</p>
            </div>
        {% endif %}
    {% endfor %}
    HTML

    Now, re-run the code and open the website, you should only see two projects. You may refer to the image below. 

  • Step 5: Deploying the Application

    Similar to what you did previously, we will deploy the application on Vercel.

    We will be using Vercel, a free application hosting service that provides support for Flask. If you have not installed the Vercel CLI utility, follow the instructions in the previous quest to install it.

    In your terminal, run the following command in the Quest2 directory: vercel

    You may refer to the following image on the options to select when deploying to Vercel.

    Next, create the file vercel.json in the Quest2 directory with the following contents.

    {
        "version": 2,
        "builds": [
          {
            "src": "app/app.py",
            "use": "@vercel/python"
          }
        ],
        "routes": [
          {
            "src": "/(.*)",
            "dest": "app/app.py"
          }
        ]
    }
    Plain text

    This file specifies that the application should be built with Python and to use “/app/app.py” as the file for the source code at any route.

    Lastly, run the vercel command again and your application should be deployed with a preview URL that is publicly available.

    Click on the Preview URL and you can see your application live. You may refer to the image below for how your application should look.

    Next, we want to ensure that our preview URL can be viewed by others. In your browser, head to Vercel and log in. Select the flask-quest2 project which you created earlier. Go to the Settings tab. Select Deployment Protection and disable Vercel Authentication. Be sure to click Save.

  • Step 6: (For Earn App) Let’s Ace Your Submissions! Preparing Your Submission!

    You have reached the end! Now to successfully complete this quest! There are 2 deliverables that are required for this quest – a screenshot and a web link.

    Before that, ⚠️ do ensure that you have input your StackUp username in all the necessary places on the website. If these are not seen in your screenshot or submitted site, you will be rejected ❌.

    Screenshot

    Take a screenshot of your code editor and your webpage side by side. Your screenshot should show:

    • your full screen, including your taskbar (for Windows and Linux) / dock (for MacOS)
    • Part of the code of index.html
    • Your preview URL shown clearly in the terminal. This same URL is also shown in your site's URL bar.
    • Your website should have only two cards - e-commerce website and flask website

    When labelling your screenshot, make sure to follow the format provided: C30_Q2_yourstackupname.png. You may also refer to the image below if you are unsure what your screenshot should look like. 

    Expected Output. Right click to open in a new tab for full resolution

    In the event you are not able to capture both your code editor and webpage in one screenshot, you can take two screenshots and merge them using this tool (credits to Stackie okitsme_parth).

    Web Link

    Submit the link to your deployed Vercel app. Ensure that your site only shows two cards and can be viewed by the public. 

    By submitting the quest, please note that our StackUp Policy prohibits the use of multiple accounts by a single user and the submission of copied work.

Have you completed this tutorial? Click below to mark as complete

Help Center Need help?

Find articles to support you through your journey or chat with our support team.

Help Center